package org.codeart.order.service;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.codeart.common.constant.IdentityEnum;
import org.codeart.common.constant.OrderStatusEnum;
import org.codeart.common.dto.Car;
import org.codeart.common.dto.OrderInfo;
import org.codeart.common.dto.PriceRule;
import org.codeart.common.request.OrderRequest;
import org.codeart.common.response.OrderDriverResponse;
import org.codeart.common.response.TerminalResponse;
import org.codeart.common.response.TraceSearchResponse;
import org.codeart.common.util.HttpStatus;
import org.codeart.common.util.R;
import org.codeart.common.util.RedisPrefixUtil;
import org.codeart.order.mapper.OrderInfoMapper;
import org.codeart.order.remote.ServiceDriverUserClient;
import org.codeart.order.remote.ServiceMapClient;
import org.codeart.order.remote.ServicePriceClient;
import org.codeart.order.remote.ServiceSseClient;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private ServicePriceClient servicePriceClient;

    @Autowired
    private ServiceDriverUserClient serviceDriverUserClient;

    @Autowired
    private ServiceMapClient serviceMapClient;

    @Autowired
    private StringRedisTemplate redisTemplate;

    public boolean add(OrderRequest orderRequest) {
        log.info("接到订单请求: {}", orderRequest);
        // 判断城市是否有可用的司机
        R<Boolean> r = serviceDriverUserClient.isAvailableDriver(orderRequest.getAddress());
        if (!r.getData()) {
            return false;
        }

        if (isOrderGoingOn(orderRequest.getPassengerId(), IdentityEnum.PASSENGER)) {
            return false;
        }

        // 判断是否在黑名单内
//        boolean flag = isBlackDevice(orderRequest.getDeviceCode());
//        if (flag) {
//            return false;
//        }

        // 判断下单的城市和计价规则是否正常
        if (!exists(orderRequest)) {
            return false;
        }

        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(orderRequest, orderInfo);
        LocalDateTime now = LocalDateTime.now();
        orderInfo.setOrderStatus(OrderStatusEnum.ORDER_START.value())
                 .setGmtCreate(now)
                 .setGmtModified(now)
                 .setVehicleType(orderRequest.getVehicleType());  // 通过用户下单添加车型
        orderInfoMapper.insert(orderInfo);

        // 定时任务派单的处理
        for (int j = 0; j < 6; j++) {
            int result = dispatchRealTimeOrder(orderInfo);
            if (result == 1) {
                return true;
            }
            if (j == 5) {
                // 订单无效
                orderInfo.setOrderStatus(OrderStatusEnum.ORDER_INVALID.value());
            } else {
                // 等待20s
                try {
                    Thread.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }


        }
        return false;
    }

    /**
     * 判断是否有在进行中的订单
     * @param id 乘客id或者司机id
     * @return boolean
     */
    private boolean isOrderGoingOn(Long id, IdentityEnum identity) {
        QueryWrapper<OrderInfo> wrapper = new QueryWrapper<>();
        if (identity == IdentityEnum.PASSENGER) {
            wrapper.lambda()
                   .eq(OrderInfo::getPassengerId, id)
                   .and(wr -> wr.eq(OrderInfo::getOrderStatus, OrderStatusEnum.ORDER_START.value())
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.DRIVER_RECEIVE_ORDER.value())
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.DRIVER_TO_PICK_UP_PASSENGER.value())
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.DRIVER_ARRIVED_DEPARTURE.value())
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.PICK_UP_PASSENGER.value())
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.PASSENGER_GET_OFF.value())
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.START_PAY.value()));
        } else {
            wrapper.lambda()
                   .eq(OrderInfo::getDriverId, id)
                   .and(wr -> wr.eq(OrderInfo::getOrderStatus, OrderStatusEnum.DRIVER_RECEIVE_ORDER.value())
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.DRIVER_TO_PICK_UP_PASSENGER.value())
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.DRIVER_ARRIVED_DEPARTURE.value())
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.PICK_UP_PASSENGER.value()));
        }

        Long i = orderInfoMapper.selectCount(wrapper);
        if (identity == IdentityEnum.DRIVER) {
            log.info("司机id: {}, 正在进行的订单数量: {}", id, i);
        }
        return i > 0;
    }

    /**
     * 获取设备编码，判断是否在黑名单内
     * @param deviceCode 设备码
     * @return boolean
     */
    private boolean isBlackDevice(String deviceCode) {
        // 生成key
        String deviceCodeKey = RedisPrefixUtil.BLACK_DEVICE_PREFIX + deviceCode;
        // 设置key
        Boolean flag = redisTemplate.hasKey(deviceCodeKey);
        if (flag) {
            String s = redisTemplate.opsForValue().get(deviceCodeKey);
            int i = Integer.parseInt(s);
            if (i >= 2) {
                return true;
            } else {
                redisTemplate.opsForValue().increment(deviceCodeKey);
            }
        } else {
            redisTemplate.opsForValue().setIfAbsent(deviceCodeKey, "1", 1L, TimeUnit.HOURS);
        }
        return false;
    }

    private boolean exists(OrderRequest orderRequest) {
        String fareType = orderRequest.getFareType();
        int separator = fareType.indexOf("$");
        String cityCode = fareType.substring(0, separator);
        String vehicleType = fareType.substring(separator + 1);

        PriceRule priceRule = new PriceRule();
        priceRule.setCityCode(cityCode).setVehicleType(vehicleType);

        R<Boolean> r = servicePriceClient.ifPriceExists(priceRule);
        return r.getData();
    }

    @Autowired
    private ServiceSseClient serviceSseClient;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 实时派单
     * @param orderInfo 订单信息
     * @return 返回1表示派单成功
     */
    public int dispatchRealTimeOrder(OrderInfo orderInfo) {
        log.info("循环一次");
        int result = 0;
        // 纬度和经度
        String depLatitude = orderInfo.getDepLatitude();
        String depLongitude = orderInfo.getDepLongitude();

        // 半径，免费接口最大半径是5000
        int[] radiusArr = {2000, 4000, 5000};
        String center = depLatitude + "," + depLongitude;

        FLAG:
        for (int radius : radiusArr) {
            R<List<TerminalResponse>> r = serviceMapClient.aroundSearch(center, radius);

            log.info("在半径: {} 内查询车辆", radius);
            // 获得终端
            List<TerminalResponse> data = r.getData();
            if (CollectionUtil.isEmpty(data)) {
                continue;
            }

            // 解析终端
            for (TerminalResponse terminalResponse : data) {
                Integer tid = terminalResponse.getTid();
                Long carId = terminalResponse.getCarId();
                String longitude = terminalResponse.getLongitude();
                String latitude = terminalResponse.getLatitude();
                log.info("车辆信息, 终端id: {}, 车辆id: {}", tid, carId);

                // 查询司机是否可以派单
                R<OrderDriverResponse> r1 = serviceDriverUserClient.getAvailableDriver(carId);
                if (r1.getCode() != HttpStatus.HTTP_OK) {
                    log.info("没有车辆id: {} 对应的司机可以派单", carId);
                } else {  // 有可用的司机
                    log.info("找到了正在出车的司机, 车辆id: {}", carId);

                    // 判断司机是否有在进行中的订单
                    OrderDriverResponse orderDriverResponse = r1.getData();
                    Long driverId = orderDriverResponse.getDriverId();
                    String driverPhone = orderDriverResponse.getDriverPhone();
                    String licenseId = orderDriverResponse.getLicenseId();
                    String vehicleNo = orderDriverResponse.getVehicleNo();
                    String vehicleTypeFromCar = orderDriverResponse.getVehicleType();

                    // 判断车辆的车型是否符合
                    String vehicleType = orderInfo.getVehicleType();
                    if (!vehicleType.trim().equals(vehicleTypeFromCar.trim())) {
                        log.info("车型不符合!");
                        continue;
                    }

                    // driverId转为字符串之后加锁，由JVM保证原子性
                    String lockKey = (driverId + "").intern();
                    RLock lock = redissonClient.getLock(lockKey);
                    lock.lock();

                    if (isOrderGoingOn(driverId, IdentityEnum.DRIVER)) {
                        lock.unlock();
                        continue;
                    }

                    // 订单直接匹配司机，查询当前车辆信息
                    QueryWrapper<Car> wrapper = new QueryWrapper<>();
                    wrapper.lambda().eq(Car::getId, carId);

                    orderInfo.setDriverId(driverId)
                             .setDriverPhone(driverPhone)
                             .setCarId(carId)
                             .setReceiveOrderCarLongitude(longitude)
                             .setReceiveOrderCarLatitude(latitude)
                             .setReceiveOrderTime(LocalDateTime.now())
                             .setLicenseId(licenseId)
                             .setVehicleNo(vehicleNo)
                             .setOrderStatus(OrderStatusEnum.DRIVER_RECEIVE_ORDER.value());

                    // 更新订单
                    orderInfoMapper.updateById(orderInfo);

                    // 通知司机
                    JSONObject content = new JSONObject();
                    content.put("passengerId", orderInfo.getPassengerId());
                    content.put("passengerPhone", orderInfo.getPassengerPhone());
                    content.put("departure", orderInfo.getDeparture());
                    content.put("depLongitude", orderInfo.getDepLongitude());
                    content.put("depLatitude", orderInfo.getDepLatitude());
                    content.put("destLongitude", orderInfo.getDestLongitude());
                    content.put("destLatitude", orderInfo.getDestLatitude());

                    // 通过SSE服务推送消息给司机端
                    serviceSseClient.push(driverId, IdentityEnum.DRIVER.value(), content.toJSONString());

                    // 通知乘客
                    JSONObject passengerContent = new JSONObject();
                    passengerContent.put("driverId", orderInfo.getDriverId());
                    passengerContent.put("driverPhone", orderInfo.getDriverPhone());
                    passengerContent.put("vehicleNo", orderInfo.getVehicleNo());

                    // 调用DriverUserService服务，查询车辆信息
                    R<Car> data1 = serviceDriverUserClient.getCarById(carId);
                    Car carRemote = data1.getData();

                    passengerContent.put("brand", carRemote.getBrand());
                    passengerContent.put("model", carRemote.getModel());
                    passengerContent.put("vehicleColor", carRemote.getVehicleColor());

                    passengerContent.put("receiveOrderCarLongitude", orderInfo.getReceiveOrderCarLongitude());
                    passengerContent.put("receiveOrderCarLatitude", orderInfo.getReceiveOrderCarLatitude());

                    log.info("推送消息给乘客端: {}", passengerContent.toJSONString());
                    serviceSseClient.push(orderInfo.getPassengerId(), IdentityEnum.PASSENGER.value(), passengerContent.toJSONString());

                    // 派单成功
                    result = 1;
                    lock.unlock();

                    // 退出不再进行司机的查找
                    break FLAG;
                }
            }
        }
        return result;
    }

    /**
     * 去接乘客
     */
    public int toPickUpPassenger(OrderRequest orderRequest) {
        Long orderId = orderRequest.getOrderId();
        LocalDateTime toPickUpPassengerTime = orderRequest.getToPickUpPassengerTime();
        String toPickUpPassengerLongitude = orderRequest.getToPickUpPassengerLongitude();
        String toPickUpPassengerLatitude = orderRequest.getToPickUpPassengerLatitude();
        String toPickUpPassengerAddress = orderRequest.getToPickUpPassengerAddress();

        QueryWrapper<OrderInfo> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(OrderInfo::getId, orderId);
        OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);

        orderInfo.setToPickUpPassengerAddress(toPickUpPassengerAddress)
                 .setToPickUpPassengerLatitude(toPickUpPassengerLatitude)
                 .setToPickUpPassengerLongitude(toPickUpPassengerLongitude)
                 .setToPickUpPassengerTime(toPickUpPassengerTime)
                 .setOrderStatus(OrderStatusEnum.DRIVER_TO_PICK_UP_PASSENGER.value());
        return orderInfoMapper.updateById(orderInfo);
    }

    /**
     * 司机到达乘客上车地点
     */
    public int arriveDeparture(OrderRequest orderRequest) {
        Long orderId = orderRequest.getOrderId();
        UpdateWrapper<OrderInfo> wrapper = new UpdateWrapper<>();
        wrapper.lambda()
               .set(OrderInfo::getDriverArrivedDepartureTime, LocalDateTime.now())
               .set(OrderInfo::getOrderStatus, OrderStatusEnum.DRIVER_ARRIVED_DEPARTURE.value())
               .eq(OrderInfo::getId, orderId);
        return orderInfoMapper.update(null, wrapper);
    }

    /**
     * 司机接到乘客
     */
    public int pickUpPassenger(@RequestBody OrderRequest orderRequest) {
        Long orderId = orderRequest.getOrderId();
        UpdateWrapper<OrderInfo> wrapper = new UpdateWrapper<>();
        wrapper.lambda()
               .set(OrderInfo::getPickUpPassengerLongitude, orderRequest.getPickUpPassengerLongitude())
               .set(OrderInfo::getPickUpPassengerLatitude, orderRequest.getPickUpPassengerLatitude())
               .set(OrderInfo::getPickUpPassengerTime, LocalDateTime.now())
               .set(OrderInfo::getOrderStatus, OrderStatusEnum.PICK_UP_PASSENGER.value())
               .eq(OrderInfo::getId, orderId);
        return orderInfoMapper.update(null, wrapper);
    }

    /**
     * 乘客下车到达目的地，行程终止
     */
    public int passengerGetOff(OrderRequest orderRequest) {
        Long orderId = orderRequest.getOrderId();
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);

        // 订单行驶的路程和时间，调用service-map
        R<Car> r = serviceDriverUserClient.getCarById(orderInfo.getCarId());
        String tid = r.getData().getTid() + "";
        Long startTime = orderInfo.getPickUpPassengerTime().toInstant(ZoneOffset.of("+8")).toEpochMilli();
//        Long endTime = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
        // 用于测试的时间，生产环境需要删除
        Long endTime = orderInfo.getPassengerGetoffTime().toInstant(ZoneOffset.of("+8")).toEpochMilli();

        R<TraceSearchResponse> r1 = serviceMapClient.traceSearch(tid, startTime, endTime);
        TraceSearchResponse searchResponse = r1.getData();

        // 获取价格
        Long driverMile = searchResponse.getDriverMile();
        Long driverTime = searchResponse.getDriverTime();
        log.info("距离: {}, 时长: {}", driverMile, driverTime);
        // address是cityCode，不是具体地名
        String address = orderInfo.getAddress();
        String vehicleType = orderRequest.getVehicleType();
        R<Double> r2 = servicePriceClient.calculatePrice(driverMile.intValue(), driverMile.intValue(), address, vehicleType);
        Double totalPrice = r2.getData();

        UpdateWrapper<OrderInfo> wrapper = new UpdateWrapper<>();
        wrapper.lambda()
               .set(OrderInfo::getPassengerGetoffTime, LocalDateTime.now())
               .set(OrderInfo::getPassengerGetoffLongitude, orderRequest.getPassengerGetOffLongitude())
               .set(OrderInfo::getPassengerGetoffLatitude, orderRequest.getPassengerGetOffLatitude())
               .set(OrderInfo::getOrderStatus, OrderStatusEnum.PASSENGER_GET_OFF.value())
               .set(OrderInfo::getDriveMile, driverMile)
               .set(OrderInfo::getDriveTime, driverTime)
               .set(OrderInfo::getPrice, totalPrice)
               .eq(OrderInfo::getId, orderId);
        return orderInfoMapper.update(null, wrapper);
    }

    /**
     * 支付完成
     */
    public boolean pay(OrderRequest orderRequest) {
        Long orderId = orderRequest.getOrderId();
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        orderInfo.setOrderStatus(OrderStatusEnum.PAY_OVER.value());
        int i = orderInfoMapper.updateById(orderInfo);
        return i != -1;
    }

    /**
     * 取消订单
     * @param orderId  订单号
     * @param identity 身份
     */
    public boolean cancel(Long orderId, String identity) {
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        Integer orderStatus = orderInfo.getOrderStatus();
        LocalDateTime cancelTime = LocalDateTime.now();
        Integer cancelTypeCode = orderInfo.getCancelTypeCode();

        // 正常取消
        int cancelType = 1;

        // 更新订单状态，如果是乘客取消
        if (identity.trim().equals(IdentityEnum.PASSENGER.value())) {
            // 订单开始
            if (orderStatus == OrderStatusEnum.ORDER_START.value()) {
                cancelTypeCode = OrderStatusEnum.CANCEL_PASSENGER_BEFORE.value();
                // 司机接到订单
            } else if (orderStatus == OrderStatusEnum.DRIVER_RECEIVE_ORDER.value()) {
                LocalDateTime receiveOrderTime = orderInfo.getReceiveOrderTime();
                long between = ChronoUnit.MINUTES.between(receiveOrderTime, cancelTime);
                if (between > 1) {
                    cancelTypeCode = OrderStatusEnum.CANCEL_PASSENGER_ILLEGAL.value();
                } else {
                    cancelTypeCode = OrderStatusEnum.CANCEL_PASSENGER_BEFORE.value();
                }
                // 司机接到乘客
            } else if (orderStatus == OrderStatusEnum.DRIVER_TO_PICK_UP_PASSENGER.value()) {
                cancelTypeCode = OrderStatusEnum.CANCEL_PASSENGER_ILLEGAL.value();
                // 司机到达上车点
            } else if (orderStatus == OrderStatusEnum.DRIVER_ARRIVED_DEPARTURE.value()) {
                cancelTypeCode = OrderStatusEnum.CANCEL_PASSENGER_ILLEGAL.value();
            } else {
                cancelType = 0;
            }
        }

        // 如果是司机取消
        if (identity.trim().equals(IdentityEnum.DRIVER.value())) {
            // 订单开始
            if (orderStatus == OrderStatusEnum.DRIVER_RECEIVE_ORDER.value()) {
                LocalDateTime receiveOrderTime = orderInfo.getReceiveOrderTime();
                long between = ChronoUnit.MINUTES.between(receiveOrderTime, cancelTime);
                if (between > 1) {
                    cancelTypeCode = OrderStatusEnum.CANCEL_DRIVER_ILLEGAL.value();
                } else {
                    cancelTypeCode = OrderStatusEnum.CANCEL_DRIVER_BEFORE.value();
                }
                // 司机去接乘客
            } else if (orderStatus == OrderStatusEnum.DRIVER_TO_PICK_UP_PASSENGER.value()) {
                cancelTypeCode = OrderStatusEnum.CANCEL_PASSENGER_ILLEGAL.value();
                // 司机到达上车点
            } else if (orderStatus == OrderStatusEnum.DRIVER_ARRIVED_DEPARTURE.value()) {
                cancelTypeCode = OrderStatusEnum.CANCEL_PASSENGER_ILLEGAL.value();
            } else {
                cancelType = 0;
            }
        }

        if (cancelType == 0) {
            return false;
        }

        orderInfo.setCancelTypeCode(cancelTypeCode)
                 .setCancelTime(cancelTime)
                 .setCancelOperator(Integer.parseInt(identity))
                 .setOrderStatus(OrderStatusEnum.ORDER_CANCEL.value());

        int i = orderInfoMapper.updateById(orderInfo);
        return i != -1;
    }

    public R<OrderInfo> detail(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        return R.ok(orderInfo);
    }

    public R<Void> pushPayInfo(OrderRequest orderRequest) {
        Long orderId = orderRequest.getOrderId();
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        orderInfo.setOrderStatus(OrderStatusEnum.START_PAY.value());
        orderInfoMapper.updateById(orderInfo);
        return R.ok();
    }

    public R<OrderInfo> current(String phone, String identity) {
        QueryWrapper<OrderInfo> wrapper = new QueryWrapper<>();
        if (identity.equals(IdentityEnum.DRIVER.value())) {
            wrapper.lambda()
                   .eq(OrderInfo::getDriverPhone, phone)
                   .and(wr -> wr.eq(OrderInfo::getOrderStatus, OrderStatusEnum.DRIVER_RECEIVE_ORDER)
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.DRIVER_TO_PICK_UP_PASSENGER)
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.DRIVER_ARRIVED_DEPARTURE)
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.PICK_UP_PASSENGER));
        }
        if (identity.equals(IdentityEnum.PASSENGER.value())) {
            wrapper.lambda()
                   .eq(OrderInfo::getPassengerPhone, phone)
                   .and(wr -> wr.eq(OrderInfo::getOrderStatus, OrderStatusEnum.ORDER_START)
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.DRIVER_RECEIVE_ORDER)
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.DRIVER_TO_PICK_UP_PASSENGER)
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.DRIVER_ARRIVED_DEPARTURE)
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.PICK_UP_PASSENGER)
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.PASSENGER_GET_OFF)
                                .or()
                                .eq(OrderInfo::getOrderStatus, OrderStatusEnum.START_PAY));
        }

        OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);
        return R.ok(orderInfo);
    }
}
